package com.aries.dubbo.like.rpc.common;

import com.aries.dubbo.like.rpc.common.enums.ResponseCodeEnum;
import com.aries.dubbo.like.rpc.common.server.ServerResponse;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 * Author: aries
 * Date: 2018/8/23
 * Description: 封装了从服务端返回的ServerResponse。
 */
@Slf4j
public class ResponseWrapper {
    private CountDownLatch countDownLatch;
    private ServerResponse serverResponse;
    /**
     * 发出请求的时间，用在 TimeOutResponseJob里，处理过期的请求和响应，防止发出请求后远程主机挂掉迟迟不返回引起内存泄漏。
     */
    @Getter
    private Long requestTime;
    @Getter
    private volatile boolean isDone;

    public ResponseWrapper() {
        this.countDownLatch = new CountDownLatch(1);
        this.requestTime = System.currentTimeMillis();
        this.isDone = false;
    }

    /**
     * 阻塞地获取serverResponse
     *
     * @return
     */
    public ServerResponse getServerResponse() {
        try {
            countDownLatch.await(20L, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException("method:ResponseWrapper.getServerResponse occurs an error", e);
        }
        ServerResponse serverResponse = this.serverResponse;
        this.isDone = true;
        return serverResponse;
    }

    /**
     * 客户端接受到服务端返回的serverResponse后，填充this.serverResponse,并唤醒被this.getServerResponse阻塞的线程
     *
     * @param serverResponse
     */
    public void setServerResponse(ServerResponse serverResponse) {
        this.serverResponse = serverResponse;
        this.countDownLatch.countDown();
    }

    /**
     * 服务端是否已经返回（for async request）
     *
     * @return
     */
    public Boolean isOk() {
        return this.countDownLatch.getCount() == 0;
    }

    /**
     * for async request（如果isOk == true后，调用该方法，不阻塞。）
     *
     * @param <T>
     * @return
     */
    public <T> T getResponseData() {
        try {
            this.countDownLatch.await(20L, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("getResponseData occurs an error:", e);
        }
        this.isDone = true;
        if (this.serverResponse.getCode() == ResponseCodeEnum.OK.getCode()) {
            return this.serverResponse.getResponseData();
        } else {
            throw new RuntimeException(" remote method invoke occurs an error：" + this.serverResponse.getErrorMessage());
        }

    }
}
